#include <driver/ide.h>
#include <os/initcall.h>
#include <os/driver.h>
#include <os/debug.h>
#include <os/memcache.h>
#include <os/gpt.h>
#include <os/mbr.h>
#include <os/hardirq.h>
#include <os/diskman.h>
#include <arch/interrupt.h>
#include <arch/pci.h>
#include <arch/io.h>
#include <arch/cpu.h>
#include <lib/type.h>
#include <sys/ioctl.h>
#include <lib/stdio.h>
#include <lib/const.h>

static uint8_t irq_invoked = 0;
static uint8_t atapi_packet[12] = {0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

static uint32_t Crc32(uint32_t crc, uint8_t data);
static int TestGptHeader(const uint8_t *header);
static int CheckDiskType(device_extension_t *devobj);
static void WriteData(ide_channel_t *channel, uint16_t data);
static uint16_t ReadData(ide_channel_t *channel);
static uint32_t WriteBuff(ide_channel_t *channel, void *buff, uint32_t bytes);
static int IdeReadSector(device_extension_t *extension, uint32_t lba, void *buff, uint32_t count);
static int IdeWriteSector(device_extension_t *extension, uint32_t lba, void *buff, uint32_t count);
static int PioDataTransfer(device_extension_t *extension, uint8_t rw, uint8_t mode, uint8_t *buff, uint32_t count);
static void ResetDriver(device_extension_t *extension);
static void DriverSoftReset(ide_channel_t *channel);
static int IdeHandler(irqno_t irq, void *data);
static int IdePrintError(device_extension_t *extension, uint32_t err);
static int IdeProbe(device_extension_t *extension, uint32_t n);
static void WriteToSector(device_extension_t *extension, void *buff, uint64_t count);
static void ReadFromSector(device_extension_t *extension, void *buff, uint64_t count);
static void SelectDisk(device_extension_t *extension, uint8_t mode, uint8_t head);
static void SendCmd(ide_channel_t *channel, uint32_t cmd);
static int IdePolling(ide_channel_t *channel, uint32_t advance);
static void SelectAddrMode(device_extension_t *extension, uint32_t lba, uint8_t *mode, uint32_t *head, uint8_t *bag);
static void SelectCmd(uint8_t op, uint8_t mode, uint8_t dma, uint8_t *cmd);
static void SelectSector(device_extension_t *extension, uint8_t mode, uint8_t *bag, uint16_t count);
static int ATAPIDeviceTransfer(device_extension_t *extension, uint8_t op, uint32_t lba, void *buff, uint32_t count);
static int ATADeviceTransfer(device_extension_t *extension, uint8_t op, uint32_t lba, void *buff, uint32_t count);
static iostatus_t DiskScanMBR(driver_object_t *driver, device_extension_t *extension);
static iostatus_t DiskScanGPT(driver_object_t *driver, device_extension_t *extension);
static int IdeCleanVol(vol_extension_t *extension, uint64_t count);

static int CheckDiskType(device_extension_t *extension)
{
    int type = -1;
    char sign[8] = {0x45, 0x47, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54};

    if(extension->type!=ATA_DEVICE)
        return type;

    uint8_t *buff = KMemAlloc(SECTOR_SIZE);
    if (!buff)
    {
        KPrint("[ide] alloc buff err\n");
        return -1;
    }
    IdeReadSector(extension, 0, buff, 1);
    if (((mbr_t *)buff)->magic == 0xaa55)
        type = DISK_MBR;
    if (memcmp(((gpt_t *)buff)->header.signature, sign, 8) == 0)
        type = DISK_GPT;
    return type;
}

static int IdeCleanVol(vol_extension_t *extension, uint64_t count)
{
    uint32_t done, todo;
    uint64_t start = extension->start_lba;

    char *buff = KMemAlloc(SECTOR_SIZE * 10); // sector limit set to 10
    if (!buff)
    {
        KPrint(PRINT_ERR "malloc mem for ide buffer failed!\n");
        return -1;
    }
    if (!count)
        count = extension->size;

    // clear buffer
    memset(buff, 0, SECTOR_SIZE * 10);
    KPrint(PRINT_DEBUG "%s: start clear count=%d\n", __func__, count);
    while (done < count)
    {
        if (done + 10 <= count)
            todo = 10;
        else
        {
            todo = count - done;
        }
        // write disk from assiant volume
        IdeWriteSector(extension->disk_object->device_extension, done + start, buff, todo);
        done += 10;
    }
    return 0;
}

static iostatus_t DiskScanMBR(driver_object_t *driver, device_extension_t *extension)
{
    int i;
    iostatus_t status = IO_SUCCESS;
    device_object_t *vol_dev = NULL;
    vol_extension_t *vol_extension = NULL;
    char volname[DEVICE_NAME_LEN+1];
    mbr_t mbr;
    mbr_partition_t *part = NULL;

    // read mbr
    IdeReadSector(extension, 0, &mbr, 1);
    if (mbr.magic != 0xaa55)
    {
        KPrint("[ide] %s: mbr no valid\n", __func__);
        return IO_FAILED;
    }

    // create logical device for any partition
    for (i = 0; i < MBR_PARTITION_MAX; i++)
    {
        part = &mbr.partition[i];

        if (part->sectors <= 0 || part->lba <= 0)
        {
            KPrint("[ide] %s: disk scan all logical disk device end!\n", __func__);
            break;
        }

        // make logical device name
        memset(volname, 0, DEVICE_NAME_LEN);
        sprintf(volname, "%s%c%d", extension->device_object->name.text, 'p', i);
        status = IoCreateDevice(driver, sizeof(vol_extension_t), volname, DEVICE_TYPE_VOL, &vol_dev);
        if (status != IO_SUCCESS)
        {
            KPrint("[ide] %s: create logical disk %s failed!\n", __func__);
            return status;
        }
        // buffer io moe
        vol_dev->flags = DEVICE_BUFFER_IO;
        vol_extension = vol_dev->device_extension;
        vol_extension->disk_object = extension->device_object;
        vol_extension->device_object = vol_dev;
        vol_extension->size = part->sectors;
        vol_extension->start_lba = part->lba;
        vol_extension->read_sectors = 0;
        vol_extension->write_sectors = 0;
        // add vol device to disk object logical device list
        extension->vol[i] = vol_dev;
        DiskAdd(vol_dev, DEVICE_TYPE_VOL);
        KPrint("[ide]  create %s logical device success. start lba %d size %d\n", volname, part->lba, part->sectors);
    }
    return IO_SUCCESS;
}

static iostatus_t DiskScanGPT(driver_object_t *driver, device_extension_t *extension)
{
    int i;
    iostatus_t status = IO_SUCCESS;
    vol_extension_t *vol_extension;
    device_object_t *vol_dev;
    char volname[DEVICE_NAME_LEN+1];
    gpt_t gpt;

    // read gpt
    IdeReadSector(extension, 0, &gpt, 1);

    for (i = 0; i < GPT_PARTION_MAX; i++)
    {
        gpt_partition_t part = gpt.partition[i];

        if (part.end_lba - part.start_lba <= 0)
        {
            KPrint("[ide] %s: disk scan all logical disk device end!\n", __func__);
            break;
        }

        // make logical device name
        sprintf(volname, "%s%d", extension->device_object->name.text, i);
        status = IoCreateDevice(driver, sizeof(vol_extension_t), volname, DEVICE_TYPE_VOL, &vol_dev);
        if (status != IO_SUCCESS)
        {
            KPrint("[ide] %s: create logical disk %s failed!\n", __func__);
            return status;
        }
        // buffer io moe
        vol_dev->flags = DEVICE_BUFFER_IO;
        vol_extension = vol_dev->device_extension;
        vol_extension->disk_object = extension->device_object; // source disk object
        string_new(&vol_extension->device_name, volname, DEVICE_NAME_LEN);
        vol_extension->device_object = vol_dev;
        vol_extension->size = part.end_lba - part.start_lba;
        vol_extension->start_lba = part.start_lba;
        vol_extension->read_sectors = 0;
        vol_extension->write_sectors = 0;
        // add vol device to disk object logical device list
        extension->vol[i] = vol_dev;
        DiskAdd(vol_dev, DEVICE_TYPE_VOL);
        KPrint("[ide] %s: create %s logical device success. start lba %d size %d \n", part.start_lba, part.end_lba - part.start_lba);
    }
}

static uint32_t Crc32(uint32_t crc, uint8_t data)
{
    uint8_t b;

    for (b = 1; b; b <<= 1)
    {
        crc ^= (data & b) ? 1 : 0;
        crc = (crc & 1) ? crc >> 1 ^ 0xEDB88320 : crc >> 1;
    }
    return crc;
}

static int GetDiskTableType(device_extension_t *extension)
{
    uint8_t buff[SECTOR_SIZE];
    uint8_t type;

    if(extension->type==ATAPI_DEVICE)
    {
        type=DISK_NONE;
        return -1;
    }

    // read first sector
    IdeReadSector(extension, 0, buff, 1);
    if (((mbr_t *)buff)->magic == 0xAA55) // check mbr
    {
        type = DISK_MBR;
    }
    else
    {
        type = DISK_NONE;
    }

    // read second sector
    IdeReadSector(extension, 1, buff, 1);
    if (TestGptHeader(buff)) // gpt header check
        type = DISK_GPT;
    return 0;
}

static int TestGptHeader(const uint8_t *header)
{
    int i, bcc;

    if (!memcmp(((gpt_header_t *)header)->signature, "EFI PART", 8))
    {
        if (((gpt_header_t *)header)->revision == 0x100)
        {
            if (((gpt_header_t *)header)->header_size == 0x5C)
            {
                for (i = 0; bcc = 0xFFFFFFFF; i < 92) // make bcc value
                {
                    bcc = Crc32(bcc, i - 16 < 4 ? 0 : header[i]);
                }
                if (~bcc != ((gpt_header_t *)header)->header_checksum)
                    return 0;
                if (((gpt_header_t *)header)->entry_size != 128) // gpt entry size
                    return 0;
                if (((gpt_header_t *)header)->entry_num > 128) // gpt entry numbers
                    return 0;
                return 1;
            }
        }
    }
    return 0;
}

static uint16_t ReadData(ide_channel_t *channel)
{
    return In16(channel->iobase + ATA_REG_DATA);
}

static void WriteData(ide_channel_t *channel, uint16_t data)
{
    Out16(channel->iobase + ATA_REG_DATA, data);
}

static uint32_t WriteBuff(ide_channel_t *channel, void *buff, uint32_t bytes)
{
    uint16_t *p = buff;
    uint32_t len = bytes;

    while (len)
    {
        WriteData(channel, *p++);
        len -= 2;
    }

    return bytes;
}

static uint32_t ReadBuff(ide_channel_t *channel, void *buff, uint32_t bytes)
{
    uint16_t *p = buff;
    uint32_t len = bytes;

    while (len)
    {
        *p++ = ReadData(channel);
        len -= 2; // read 2 bytes
    }
    return bytes;
}

// send reset to devie control register
static void DriverSoftReset(ide_channel_t *channel)
{
    uint8_t data = In8(channel->iobase + ATA_REG_CTRL);
    // send reset cammand
    Out8(channel->iobase + ATA_REG_CTRL, ATA_CONTROL_SRST);
    // wait reset
    for (int i = 0; i < 50; i++)
    {
        In8(channel->iobase + ATA_REG_ALTSTU);
    }
    // reset finish,refresh status
    Out8(channel->iobase + ATA_REG_CTRL, data);
}

// reset driver
static void ResetDriver(device_extension_t *extension)
{
    DriverSoftReset(extension->channel);
}

static int IdeHandler(irqno_t irq, void *data)
{
    ide_channel_t *channel = data;
    device_extension_t *extension = channel->extension;

    if (In8(channel->iobase + ATA_REG_STATUS) & ATA_STATUS_ERR)
    {
        ResetDriver(extension);
    }

    if (channel->curop == IDE_READ)
    {
        extension->read_sectors++;
    }
    else
    {
        extension->write_sectors++;
    }
    return 0;
}

// ide print error message to screen
static int IdePrintError(device_extension_t *extension, uint32_t err)
{
    uint8_t status;

    // no avaliable error
    if (!err)
        return err;

    // device fault
    if (err == 1)
        KPrint("Device Fault\n");
    else
    {
        // error
        if (err == 2)
        {
            status = In8(extension->channel->iobase + ATA_REG_STATUS);
            if (status & ATA_ER_AMNF)
                KPrint("No address Mask Found\n");
            if (status & ATA_ER_TK0NF)
                KPrint("No Media or Media error\n");
            if (status & ATA_ER_ABRT)
                KPrint("Command Abort\n");
            if (status & ATA_ER_MCR)
                KPrint("No Media or Media error\n");
            if (status & ATA_ER_IDNF)
                KPrint("ID mask no Found\n");
            if (status & ATA_ER_MC)
                KPrint("No Media or Media error\n");
            if (status & ATA_ER_UNC)
                KPrint("Uncorrectable data error\n");
            if (status & ATA_ER_BBK)
                KPrint("Bad Sector\n");
        }
        else if (err == 3)
            KPrint("Read Notings\n");
        else if (err == 4)
            KPrint("Write Protect\n");
        else if (err == 5)
            KPrint("Timeout\n");
        // printf channel
        switch (extension->channel - channelinfo)
        {
        case ATA_PRIMARY_CHANNEL:
            KPrint("ATA Primary Channel\n");
            break;
        case ATA_SLAVE_CHANNEL:
            KPrint("ATA Slave Channel\n");
        default:
            break;
        }
        // printf driver
        switch (extension->driver)
        {
        case ATA_MASTER_DEVICE:
            KPrint("ATA Master Device\n");
            break;
        case ATA_SLAVE_DEVICE:
            KPrint("ATA Slave Device\n");
        default:
            break;
        }
    }
    return err;
}

// ide probe device
static int IdeProbe(device_extension_t *extension, uint32_t n)
{
    uint32_t channel_id = n / 2;
    uint32_t disk_id = n % 2;
    ide_channel_t *channel;
    char irqname[32];
    uint8_t err;
    uint8_t type;
    uint8_t cl, ch;

    // get targe device channel object
    channel = &channelinfo[channel_id];

    // init channel info and irq for to every channel
    switch (channel_id)
    {
        // primary channel
    case ATA_PRIMARY_CHANNEL:
    {
        channel->iobase = ATA_PRIMARY_CMDREG_BA;
        channel->ctrlbase = ATA_PRIMARY_ALTREG_BA;
        channel->irqno = IRQ14_HARDDISK1;
    }
    break;
    // slave channel
    case ATA_SLAVE_CHANNEL:
    {
        channel->iobase = ATA_SLAVE_CMDREG_BA;
        channel->ctrlbase = ATA_SLAVE_ALTREG_BA;
        channel->irqno = IRQ15_HARDDISK2;
    }
    break;
    }
    // register interrrupt for channel
    if (!disk_id)
    {
        sprintf(irqname, "harddisk channel%d", channel_id);
        IrqRegister(channel->irqno, IdeHandler, IRQ_DISABLE, "harddisk", irqname, channel);
    }
    // init to 0
    channel->curop = 0;
    channel->curactive = 0;
    // set extension info
    channel->extension = extension;
    extension->channel = channel;
    extension->driver = disk_id;
    extension->info = KMemAlloc(SECTOR_SIZE);
    if (!extension->info)
    {
        IrqUnregister(channel->irqno, channel);
        return -1;
    }
    // reset driver
    ResetDriver(extension);
    // select disk info
    SelectDisk(extension, 0, 0);

    int timeout = 1000; // wait timeout
    // wait disk ready
    while (!(In8(channel->iobase + ATA_REG_STATUS) & ATA_STATUS_READY) && (--timeout))
        ;
    if (timeout <= 0)
    {
        KPrint(PRINT_ERR "[ide]disk %d maybe no ready or not exist\n", n);
        IrqUnregister(channel->irqno, channel);
        return -1;
    }

    // detemine device type
    type = ATA_DEVICE;
    // send IDENTIFY cmd
    SendCmd(channel, ATA_CMD_IDENTIFY);
    err = IdePolling(channel, 1);
    if (err)
    {
        // probe ATAPI device
        cl = In8(channel->iobase + ATA_REG_LBA1);
        ch = In8(channel->iobase + ATA_REG_LBA2);
        if (cl == 0x14 && ch == 0xEB)
            type = ATAPI_DEVICE;
        else
        {
            if (cl == 0x69 && ch == 0x96)
                type = ATAPI_DEVICE;
            else
            {
                IdePrintError(extension, err);
                IrqUnregister(channel->irqno, channel);
                KMemFree(extension->info);
                return -1;
            }
        }
        SendCmd(channel, ATA_CMD_IDENTIFY_PACKET); // send ATAPI identify cmd
        err = IdePolling(channel, 1);
        if (err)
        {
            IdePrintError(extension, err);
            IrqUnregister(channel->irqno, channel);
            KMemFree(extension->info);
            return -1;
        }
    }
    extension->type = type;
    // read ata device identify
    ReadFromSector(extension, extension->info, 1);
    // set extension info
    extension->command_sets = (((int)extension->info->cmdSet1 << 16) + extension->info->cmdSet0);
    if (extension->command_sets & (1 << 26))
    {
        KPrint("[ide] LBA48 support\n");
        // LBA48 address
        extension->size = ((int)extension->info->lba48Sectors[1] << 16) +
                          (int)extension->info->lba48Sectors[0];
    }
    else
    {
        KPrint("[ide] LBA28/CHS support\n");
        // CHS or LBA28
        extension->size = ((int)extension->info->lba28Sectors[1] << 16) +
                          (int)extension->info->lba28Sectors[0];
    }
    extension->capabilities = extension->info->Capabilities0;
    extension->signature = extension->info->General_Config;
    extension->exist = 1; // device exist
    extension->rwoff = 0;
    extension->disk_type = CheckDiskType(extension);
    int i=0;
    for(i=0;i<40;i+=2)
    {
        extension->model[i]=((char*)(extension->info->Model_Number))[i+1];
        extension->model[i+1]=((char*)(extension->info->Model_Number))[i];
    }
    extension->model[i]=0;
    KMemFree(extension->info); //free memory

    KPrint("[ide] probe type %x size %d boot type %x Name %s\n", extension->type, extension->size, (uint32_t)extension->disk_type,extension->model);
    return 0;
}

static void WriteToSector(device_extension_t *extension, void *buff, uint64_t count)
{
    uint64_t bytes;

    if (!count)
    {
        bytes = SECTOR_SIZE;
    }
    else
    {
        bytes = SECTOR_SIZE * count;
    }
    WriteBuff(extension->channel, buff, bytes);
}

static void ReadFromSector(device_extension_t *extension, void *buff, uint64_t count)
{
    uint64_t bytes;

    if (!count)
        bytes = SECTOR_SIZE;
    else
        bytes = SECTOR_SIZE * count;
    ReadBuff(extension->channel, buff, bytes);
}

// send cmd to ide channel
static void SendCmd(ide_channel_t *channel, uint32_t cmd)
{
    Out8(channel->iobase + ATA_REG_CMD, cmd);
}

// busy wait
// 0: wait no error
// 1: device fault
// 2: status error
// 5: timeout
static uint8_t BusyWait(device_extension_t *extension)
{
    ide_channel_t *channel = extension->channel;
    uint32_t time_limit = 1000 * 1000;
    uint8_t status;

    while (time_limit--)
    {
        // read status register until ATA_STATUS_BUSY clear
        if (!(In8(channel->iobase + ATA_REG_STATUS) & ATA_STATUS_BUSY))
        {
            status = In8(channel->iobase + ATA_REG_STATUS);
            if (status & ATA_STATUS_ERR)
            {
                return 2;
            }
            if (status & ATA_STATUS_DEVFAULT)
            {
                return 1;
            }
            // data request
            if (status & ATA_STATUS_DRQ)
            {
                return 0;
            }
        }
        else
        {
            In8(channel->iobase + ATA_REG_STATUS);
        }
    }
    return 5;
}

static int ATADeviceTransfer(device_extension_t *extension, uint8_t op, uint32_t lba, void *buff, uint32_t count)
{
    ide_channel_t *channel = extension->channel;
    uint32_t done = 0, less = 0;
    uint8_t mode; // mode 0: CHS 1: LBA28 2: LBA48
    uint8_t cmd;
    uint8_t lbaio[6];
    uint8_t dma; // support dma mode
    uint8_t err;
    uint32_t head;

    // save operator
    channel->curop = op;

    while (done < count)
    {
        // max opeartor 256 sectors on once transfer
        if (done + 256 <= count)
            less = 256;
        else
            less = count - done;

        // dma support
        dma = 0;
        // select address mode
        SelectAddrMode(extension, lba + done, &mode, &head, lbaio);
        // waitting driver
        while (In8(channel->iobase + ATA_REG_STATUS) & ATA_STATUS_BUSY)
            CpuIdle();
        // select disk
        SelectDisk(extension, mode, head);
        // select sector
        SelectSector(extension, mode, lbaio, count);

        // wait disk controller in ready status
        while (!(In8(channel->iobase + ATA_REG_STATUS) & ATA_STATUS_READY))
            CpuIdle();
        // select and send cmd
        SelectCmd(op, mode, dma, &cmd);
        // send command
        SendCmd(channel, cmd);

        // wait disk controller in ready status
        while (!(In8(channel->iobase + ATA_REG_STATUS) & ATA_STATUS_READY))
            CpuIdle();

        // transfer data according mode
        if (dma)
        {
            // DMA mode transfer
            /** @todo: support dma mode **/
            if (op == IDE_READ)
            {
            }
            else
            {
            }
        }
        else
        {
            // PIO transfer
            if (err = PioDataTransfer(extension, op, mode, buff, less))
                return err;
            // update data
            buff += less * SECTOR_SIZE;
            done += less;
        }
    }
    return 0;
}

static int IdeReadSector(device_extension_t *extension, uint32_t lba, void *buff, uint32_t count)
{
    uint8_t err = 0;

    if(extension->type!=ATA_DEVICE)
        return -1;

    if (lba + count >= extension->size && extension->type == ATA_DEVICE)
    {
        KPrint(PRINT_ERR "%s: ide read err!\n", __func__);
        return -1;
    }

    // read sector from ide
    for (int i = 0; i < count; i++)
    {
        // according device type transfer
        err = ATADeviceTransfer(extension, IDE_READ, lba + i, buff + i * SECTOR_SIZE, 1);
        if (IdePrintError(extension, err))
        {
            KPrint(PRINT_ERR "%s ide read err!\n", __func__);
            return -1;
        }
    }
    return 0;
}

static int ATAPIDeviceTransfer(device_extension_t *extension, uint8_t op, uint32_t lba, void *buff, uint32_t count)
{
    return 0;
}

static int IdeWriteSector(device_extension_t *extension, uint32_t lba, void *buff, uint32_t count)
{
    uint8_t err;

    if (lba + count >= extension->size && extension->type == ATA_DEVICE)
    {
        KPrint(PRINT_ERR "%s: ide write err!\n", __func__);
        return -1;
    }
    // ATAPI no support write
    if (extension->type == ATAPI_DEVICE)
    {
        KPrint(PRINT_ERR "%s: ide write protect!\n", __func__);
        return -1;
    }
    // write sector to ide
    for (int i = 0; i < count; i++)
    {
        err = ATADeviceTransfer(extension, IDE_WRITE, lba + i, buff + i * SECTOR_SIZE, 1);
        if (IdePrintError(extension, err))
        {
            KPrint(PRINT_ERR "%s ide write err!\n", __func__);
            return -1;
        }
    }
    return 0;
}

static int IdeCleanDisk(device_extension_t *extension, uint32_t count)
{
    uint32_t done, todo;
    char *buff = KMemAlloc(SECTOR_SIZE * 10); // sector limit set to 10
    if (!buff)
    {
        KPrint(PRINT_ERR "malloc mem for ide buffer failed!\n");
        return -1;
    }
    if (!count)
        count = extension->size;

    // clear buffer
    memset(buff, 0, SECTOR_SIZE * 10);

    KPrint(PRINT_DEBUG "%s: start clear count=%d\n", __func__, count);
    while (done < count)
    {
        if (done + 10 <= count)
            todo = 10;
        else
        {
            todo = count - done;
        }
        IdeWriteSector(extension, done, buff, todo);
        done += 10;
    }
    return 0;
}

/**
 * select sectors and sector number
 * @extension: device
 * @mode: transfer mode(CHS or LBA)
 * @lba: lba address bag
 * @count: sector count
 * */
static void SelectSector(device_extension_t *extension, uint8_t mode, uint8_t *bag, uint16_t count)
{
    ide_channel_t *channel = extension->channel;

    // lba48
    if (mode == 2)
    {
        Out8(channel->iobase + ATA_REG_FEATURE, 0); // PIO mode

        // set sector counts
        Out8(channel->iobase + ATA_REG_SECCNT1, 0);
        // set lba address[bits24-47]
        Out8(channel->iobase + ATA_REG_LBA3, bag[3]);
        Out8(channel->iobase + ATA_REG_LBA4, bag[4]);
        Out8(channel->iobase + ATA_REG_LBA5, bag[5]);
    }

    // lba28
    Out8(channel->iobase + ATA_REG_FEATURE, 0); // PIO mode
    // set sector counts
    Out8(channel->iobase + ATA_REG_SECCNT0, count & 0xff);
    // set lba address [bits0-23]
    Out8(channel->iobase + ATA_REG_LBA0, bag[0]);
    Out8(channel->iobase + ATA_REG_LBA1, bag[1]);
    Out8(channel->iobase + ATA_REG_LBA2, bag[2]);
}

// select address mode
static void SelectAddrMode(device_extension_t *extension, uint32_t lba, uint8_t *mode, uint32_t *head, uint8_t *bag)
{
    uint32_t sector, cylider;

    if (lba >= 0x10000000 && extension->capabilities & 0x200)
    {
        // LBA48
        *mode = 2;
        bag[0] = (lba & 0x000000ff) >> 0;
        bag[1] = (lba & 0x0000ff00) >> 8;
        bag[2] = (lba & 0x00ff0000) >> 16;
        bag[3] = (lba & 0xff000000) >> 24;
        bag[4] = 0;
        bag[5] = 0;
        *head = 0; // low 4bits of HDDVSEL are no used
    }
    else
    {
        if (extension->capabilities & 0x200)
        {
            // lba28
            *mode = 1;
            bag[0] = (lba & 0x00000ff) >> 0;
            bag[1] = (lba & 0x000ff00) >> 8;
            bag[2] = (lba & 0x0ff0000) >> 16;
            bag[3] = 0;
            bag[4] = 0;
            bag[5] = 0;
            *head = (lba & 0xf000000) >> 24;
        }
        else
        {
            // CHS
            *mode = 0;
            sector = (lba % 63) + 1;
            cylider = (lba + 1 - sector) / (16 * 63);
            bag[0] = sector;
            bag[1] = (cylider)&0xFF;
            bag[2] = (cylider >> 8) & 0xFF;
            bag[3] = 0;
            bag[4] = 0;
            bag[5] = 0;
            *head = (lba + 1 - sector) % (16 * 63) / (63);
        }
    }
}

// select targe disk
static void SelectDisk(device_extension_t *extension, uint8_t mode, uint8_t head)
{
    Out8(extension->channel->iobase + ATA_REG_HDDSEL, ATA_DEVICE_MASK((!mode) ? 0 : 1, extension->driver, head));
    extension->channel->curactive = extension->driver;
}

// according op,mode and dma select command
static void SelectCmd(uint8_t op, uint8_t mode, uint8_t dma, uint8_t *cmd)
{
    if (mode == 0 && dma == 0 && op == IDE_READ)
        *cmd = ATA_CMD_READ_PIO;
    if (mode == 1 && dma == 0 && op == IDE_READ)
        *cmd = ATA_CMD_READ_PIO;
    if (mode == 2 && dma == 0 && op == IDE_READ)
        *cmd = ATA_CMD_READ_PIO_EXT;
    if (mode == 0 && dma == 0 && op == IDE_WRITE)
        *cmd = ATA_CMD_WRITE_PIO;
    if (mode == 1 && dma == 0 && op == IDE_WRITE)
        *cmd = ATA_CMD_WRITE_PIO;
    if (mode == 2 && dma == 0 && op == IDE_WRITE)
        *cmd = ATA_CMD_WRITE_PIO_EXT;

    if (mode == 0 && dma == 1 && op == IDE_READ)
        *cmd = ATA_CMD_READ_DMA;
    if (mode == 1 && dma == 1 && op == IDE_READ)
        *cmd = ATA_CMD_READ_DMA;
    if (mode == 2 && dma == 1 && op == IDE_READ)
        *cmd = ATA_CMD_READ_DMA_EXT;
    if (mode == 0 && dma == 1 && op == IDE_WRITE)
        *cmd = ATA_CMD_WRITE_DMA;
    if (mode == 1 && dma == 1 && op == IDE_WRITE)
        *cmd = ATA_CMD_WRITE_DMA;
    if (mode == 2 && dma == 1 && op == IDE_WRITE)
        *cmd = ATA_CMD_WRITE_DMA_EXT;
}

static int PioDataTransfer(device_extension_t *extension, uint8_t rw, uint8_t mode, uint8_t *buff, uint32_t count)
{
    volatile uint16_t i = 0;
    uint8_t err;

    if (rw == IDE_READ)
    {
        while (i < count)
        {
            if ((err = BusyWait(extension)))
            {
                KPrint(PRINT_ERR "wait driver failed! err: 0x%x\n", err);
                ResetDriver(extension); // reset driver
            }
            // read data from sector
            ReadFromSector(extension, buff, 1);
            ++i;
            buff += SECTOR_SIZE;
        }
    }
    else
    {
        while (i < count)
        {
            if ((err = BusyWait(extension)))
            {
                KPrint(PRINT_ERR "wait driver failed!\n");
                ResetDriver(extension); // reset driver
            }
            // write data to sector
            WriteToSector(extension, buff, 1);
            ++i;
            buff += SECTOR_SIZE;
        }
        // flush write buffer
        Out8(extension->channel->iobase + ATA_REG_CMD, mode >= 1 ? ATA_CMD_CACHE_FLUSH_EXT : ATA_CMD_CACHE_FLUSH);
        IdePolling(extension->channel, 0);
    }
    return 0;
}

// ide polling
// When we send a command, we should wait for
// 400 nanosecond, then read the Status port.
// If the Busy bit is on, we should read the
// status port again until the Busy bit is 0;
// then we can read the results of the command.
// This operation is called "Polling".
static int IdePolling(ide_channel_t *channel, uint32_t advance)
{
    int i;
    uint8_t status;

    // read alternate status register waitting 400ns,read once just cost 100ns
    for (int i = 0; i < 4; i++)
    {
        In8(channel->iobase + ATA_REG_ALTSTU);
    }
    // wait for status register BSY to be clear
    while (In8(channel->iobase + ATA_REG_STATUS) & ATA_STATUS_BUSY)
        ;

    if (advance)
    {
        status = In8(channel->iobase + ATA_REG_STATUS);
        // error check
        if (status & ATA_STATUS_ERR)
        {
            return 2;
        }
        // check if device fault
        if (status & ATA_STATUS_DEVFAULT)
        {
            return 1;
        }
        // check if data request ready
        if (!(status & ATA_STATUS_DRQ))
        {
            return 3;
        }
    }
    return 0;
}

static iostatus_t IdeRead(device_object_t *device, io_request_t *ioreq)
{
    iostatus_t status = IO_SUCCESS;
    uint32_t sectors = DIV_ROUND_UP(ioreq->parame.read.len, SECTOR_SIZE);
    device_extension_t *extension = device->device_extension;
    uint64_t off;

    // get offset
    if (ioreq->parame.read.offset == DISKOFF_MAX)
    {
        off = extension->rwoff; // from rwoffset start
    }
    else
    {
        off = ioreq->parame.read.offset;
    }

    // logical volume device
    if (device->type == DEVICE_TYPE_VOL)
    {
        vol_extension_t *vol_ext = (vol_extension_t *)extension;
        if (off >= vol_ext->size)
            off = vol_ext->rwoff;
        off += vol_ext->start_lba;                          // from volume start lba start
        extension = vol_ext->disk_object->device_extension; // translate to disk extension
    }

    // read disk
    uint32_t len = IdeReadSector(extension, off, ioreq->sys_buff, sectors);
    if (len >= 0) // read succeed
    {
        len = sectors * SECTOR_SIZE;
    }
    else // failed
    {
        status = IO_FAILED;
    }

    ioreq->io_status.status = status;
    ioreq->io_status.info = len;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t IdeWrite(device_object_t *device, io_request_t *ioreq)
{
    iostatus_t status = IO_SUCCESS;
    uint32_t sectors = DIV_ROUND_UP(ioreq->parame.write.len, SECTOR_SIZE);
    device_extension_t *extension = device->device_extension;
    uint64_t off;
    uint64_t len;

    // get offset
    if (ioreq->parame.read.offset == DISKOFF_MAX)
    {
        off = extension->rwoff; // reset rwoffset
    }
    else
    {
        off = ioreq->parame.read.offset;
    }

    // logical voluem device
    if (device->type == DEVICE_TYPE_VOL)
    {
        vol_extension_t *vol_ext = (vol_extension_t *)extension;
        if (off >= vol_ext->size)
            off = vol_ext->rwoff;
        off += vol_ext->start_lba;
        extension = vol_ext->disk_object->device_extension; // translate to disk extension
    }

    // write disk
    len = IdeWriteSector(extension, off, ioreq->sys_buff, sectors);
    if (!len) // read succeed
    {
        len = sectors * SECTOR_SIZE;
    }
    else // failed
    {
        status = IO_FAILED;
    }

    ioreq->io_status.status = status;
    ioreq->io_status.info = len;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t IdeDevctl(device_object_t *device, io_request_t *ioreq)
{
    iostatus_t status = IO_SUCCESS;
    int info;
    device_extension_t *extension = device->device_extension;
    uint32_t cmd = ioreq->parame.devctl.code;
    uint32_t arg = ioreq->parame.devctl.arg;

    switch (cmd)
    {
    case DISKIO_GETSIZE:
    {
        if (device->type == DEVICE_TYPE_VOL)
            *(uint32_t *)arg = ((vol_extension_t *)extension)->size;
        else
            *(uint32_t *)arg = extension->size;
    }
    break;
    case DISKIO_CLEAN:
    {
        if (device->type == DEVICE_TYPE_VOL)
            IdeCleanVol((vol_extension_t *)device->device_extension, arg);
        else
            IdeCleanDisk((device_extension_t *)device->device_extension, arg);
    }
    break;
    case DISKIO_SETOFF:
    {
        uint32_t off = *(uint32_t *)arg;

        if (device->type == DEVICE_TYPE_VOL)
        {
            if (off > ((vol_extension_t *)extension)->size - 1)
                off = ((vol_extension_t *)extension)->size - 1;
            ((vol_extension_t *)extension)->rwoff = off;
        }
        else
        {
            if (off > extension->size - 1)
                off = extension->size - 1;
            extension->rwoff = off;
        }
    }
    break;
    case DISKIO_GETOFF:
    {
        if (device->type == DEVICE_TYPE_VOL)
            *(uint32_t *)arg = ((vol_extension_t *)extension)->rwoff;
        else
            *(uint32_t *)arg = extension->rwoff;
    }
    break;
    case DISKIO_GET_DISKTYPE:
    {
        if (device->type != DEVICE_TYPE_VOL)
            *(uint32_t *)arg = extension->disk_type;
        else
            status = IO_FAILED;
    }
    break;
    case DISKIO_GETMODEL:
    {
        if(device->type!=DEVICE_TYPE_DISK)
            status=IO_FAILED;
        else 
            memcpy((void*)arg,extension->model,40);
    }
    break;
    default:
        status = IO_FAILED;
    }
    ioreq->io_status.status = status;
    ioreq->io_status.info = info;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t IdeOpen(device_object_t *devobj, io_request_t *ioreq)
{
    iostatus_t status = IO_SUCCESS;

    ioreq->io_status.status = status;
    ioreq->io_status.info = 0;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t IdeClose(device_object_t *devobj, io_request_t *ioreq)
{
    iostatus_t status = IO_SUCCESS;

    ioreq->io_status.status = status;
    ioreq->io_status.info = 0;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t IdeEnter(driver_object_t *driver)
{
    iostatus_t status = IO_SUCCESS;
    device_object_t *device;
    device_extension_t *extension;
    int i = 0, diskid = 0;
    char name[DEVICE_NAME_LEN+1];
    uint8_t found = *(uint8_t *)IDE_DISK_NUM + 1;
    KPrint("[ide] system found %d disks\n", found);

    // ide driver max have 4 disk
    for (i = 0; i < found; i++)
    {
        memset(name, 0, DEVICE_NAME_LEN);
        sprintf(name, "%s%d", DEVICE_NAME, diskid++);

        status = IoCreateDevice(driver, sizeof(device_extension_t), name, DEVICE_TYPE_DISK, &device);
        if (status != IO_SUCCESS)
        {
            KPrint("[driver] %s: create device failed!\n", __func__);
            return status;
        }
        // buffer io mode
        device->flags = DEVICE_BUFFER_IO;
        extension = device->device_extension;
        extension->read_sectors = 0;
        extension->write_sectors = 0;
        extension->rwoff = 0;
        extension->device_object = device;

        // probe ide device
        if (IdeProbe(extension, i) < 0) // try probe ide disk info
        {
            diskid--;
            IoDeleteDevice(device);
            status = IO_FAILED;
        }
        else
        {
            // no ATA device
            if (extension->type != ATA_DEVICE)
            {
                diskid--;
                IoDeleteDevice(device);
                status = IO_FAILED;
            }
            else
            {
                found++;

                // disk device create then create logical device
                KPrint("[driver] found ata device %s success!\n", name);
                // scan all partition
                if (extension->disk_type == DISK_MBR)
                {
                    KPrint("[driver] scan mbr\n");
                    DiskScanMBR(driver, extension);
                }
                else
                {
                    if (extension->disk_type == DISK_GPT)
                    {
                        KPrint("[driver] scan gpt\n");
                        DiskScanGPT(driver, extension);
                    }
                }
                DiskAdd(device, DEVICE_TYPE_DISK);
                status = IO_SUCCESS;
            }
        }
    }

    if (found < 1)
    {
        KPrint("[ide] no found ide device!\n");
        status = IO_FAILED;
    }
    else
    {
        status = IO_SUCCESS;
    }

    return status;
}

static iostatus_t IdeExit(driver_object_t *driver)
{
    device_object_t *device, *next;
    list_traversal_all_owner_to_next_safe(device, next, &driver->device_list, list)
    {
        list_del_init(&device->list);
    }
    string_del(&driver->name); // delete driver name
}

static iostatus_t IdeDriverFun(driver_object_t *driver)
{
    iostatus_t status = IO_SUCCESS;

    // bind driver
    driver->driver_enter = IdeEnter;
    driver->driver_exit = IdeExit;
    // dispatch function
    driver->dispatch_fun[IOREQ_OPEN] = IdeOpen;
    driver->dispatch_fun[IOREQ_CLOSE] = IdeClose;
    driver->dispatch_fun[IOREQ_READ] = IdeRead;
    driver->dispatch_fun[IOREQ_WRITE] = IdeWrite;
    driver->dispatch_fun[IOREQ_DEVCTL] = IdeDevctl;
    // driver name
    string_new(&driver->name, DRIVER_NAME, DRIVER_NAME_LEN);

    return status;
}

static void __init IdeDriverEntry(void)
{
    KPrint("[driver] create ide driver\n");
    if (DriverObjectCreate(IdeDriverFun))
    {
        KPrint(PRINT_ERR "[driver] %s create driver object faild1\n", DRIVER_NAME);
    }
}

driver_initcall(IdeDriverEntry);